/// @file rootedtree.cc
/// @brief Rooted tree class definitions

// *************************************************************************

// Copyright© 2010 Texas A&M University, 
//                 College Station, Texas
//                 Contact: R Crosby <rwc@cs.tamu.edu>
//
// Copyright© 2003, 2004 by BiRC -- Bioinformatics Research Center
//                 University of Aarhus, Denmark
//                 Contact: Thomas Mailund <mailund@birc.dk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// *************************************************************************

#ifndef ROOTEDTREE_HH
#define ROOTEDTREE_HH

#include <cassert>
#include <list>
#include <stack>
#include <string>
#include <vector>

using namespace std;

#include "unrootedtree.hh"

class RootedTreeVisitor;

// *****************************************************************************

class RootedTreeNode 
{
private:
public:
    virtual ~RootedTreeNode() {}
    //FIXME a bit ugly - since a leaf has this one aswell
    virtual void add_child(RootedTreeNode *child) {};
    virtual void accept(RootedTreeVisitor *visitor) = 0;
};

// *****************************************************************************

class RootedTreeRoot : public RootedTreeNode 
{
private:

    RootedTreeNode *_child;
    S *_s;

public:

    RootedTreeRoot ( S* s)
	: _child(0), _s(s) {}

    void            accept    ( RootedTreeVisitor *visitor,
				const char * fn, 
				const unsigned ln );
    void            accept    ( RootedTreeVisitor *visitor )
	{ assert(1); }

    RootedTreeNode* child     () { return _child; }
    void            add_child ( RootedTreeNode *child );
    S *             s         () { return _s; }

};

// *****************************************************************************

class RootedTreeInner : public RootedTreeNode 
{
private:
    RootedTreeNode *_parent;
    RootedTreeNode *_left;
    RootedTreeNode *_right;
public:
    RootedTreeInner() : _parent(0), _left(0), _right(0) {}
    void accept(RootedTreeVisitor *visitor);
    void add_child(RootedTreeNode *child);
    void parent(RootedTreeNode *p) { _parent = p; }
    RootedTreeNode *left() { return _left; }
    RootedTreeNode *right() { return _right; }
};

// *****************************************************************************

class RootedTreeLeaf : public RootedTreeNode 
{
private:
    RootedTreeNode *_parent;
    S *_s;
public:
    RootedTreeLeaf(S *s) { _s = s; }
    void accept(RootedTreeVisitor *visitor);
    //ugly
    void add_child(RootedTreeNode *child);
    void parent(RootedTreeNode *p) { _parent = p; }
    S *s() { return _s; }
};

// *****************************************************************************

class RootedTree {
private:

    friend class BuildRootedTreeVisitor;

    list<RootedTreeNode *> _nodes;
    list<RootedTreeLeaf *> _leafs;
    RootedTreeRoot*        _root;
    
public:

    RootedTree  ( UnrootedTree*  ut, 
		  string         s );
    ~RootedTree ();

    RootedTreeRoot*         root()                     { return _root; }
    list<RootedTreeLeaf *>& leafs()                    { return _leafs; }
    list<RootedTreeNode *>& nodes()                    { return _nodes; }
    void                    print(ostream &os) const;

};

// *****************************************************************************
    
class RootedTreeVisitor {

public:

    virtual ~RootedTreeVisitor() {}
    virtual void visit_root  ( RootedTreeRoot *root,
			       const char *    fn,
			       const unsigned  ln )     = 0;
    virtual void visit_leaf  ( RootedTreeLeaf *leaf )   = 0;
    virtual void visit_inner ( RootedTreeInner *inner ) = 0;

};

#endif // ROOTEDTREE_HH
